`timescale 1ns / 1ps 
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/03/01 17:18:09
// Design Name:
// Module Name: Sdram_Control_2Port
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


module Sdram_Control_2Port(
           //host side
           REF_CLK,                       //sdram control clock
           OUT_CLK,                       //sdram output clock
           RESET_N,                       //global clock reset

           //fifo write side 1
           wdat1,
           wr1,
           waddr1_min,
           waddr1_max,
           wr1_length,
           wr1_load,
           wr1_clk.Sdram_Control_2Port,
           // fifo write side 1
           wdat2,
           wr2,
           waddr2_min,
           waddr2_max,
           wr2_length,
           wr2_load,
           wr2_clk,

           //fifo read side 1
           rdat1,
           rd1,
           raddr_min,
           raddr_max,
           rd1_length,
           rd1_load,
           rd1_clk,

           // fifo read side 2
           rdat2,
           rd2,
           raddr2_min,
           raddr2_max,
           rd2_length,
           rd2_load,
           rd2_clk,

           //sdram side
           SA,
           BA,
           CS_N,
           CKE,
           RAS_N,
           CAS_N,
           WE_N,
           DQ,
           DQM,
           SDR_CLK,

           // user interface add by crazybingo
           Sdram_Init_Done,                      //sdram init done signal
           Sdram_Read_Valid  //sdram resd valid:output

       );
`include "Sdram_Params.vh"
parameter DSIZE = 9;
parameter ASIZE = 9;
parameter ROWSIZE = 9;

//host side
input REF_CLK; //sdram control clock
input OUT_CLK; //sdram output clock
input RESET_N; //system reset

//fifo write side 1
input [DSIZE - 1: 0] wdat1; //data input
input wr1; //write request
input [ASIZE - 1: 0] waddr_min; //write start address
input [ASIZE - 1: 0] waddr_max; //write max address
input [8: 0] wr1_length; //write length
input wr1_load; //write register load&fifo clear
input wr1_clk; //write fifo clock

//fifo write side 2
input [DSIZE - 1: 0] wdat2; //data input
input wr2; //WRITE REQUEST
input [ASIZE - 1: 0] waddr2_min; //write start address
input [ASIZE - 1: 0] waddr2_max; //write max address
input [8: 0] wr2_length; //write length'
input wr2_load; //writeb register load&fifo clear
input wr2_clk; //write fifo clock

//fifo read side 1
output [7: 0] rdat1; //data output
input rd1; //read request
input [ASIZE - 1: 0] raddr1_min; //read start address
input [ASIZE - 1: 0] raddr1_max; //read max address
input [8: 0] rd1_length; //read length
input rd1_load; //read register load&fifo clear
input rd1_clk; //read fifo clock

//fifo read side 2
output [15: 0] rdat; //data output
input rd2; //read request
input [ASIZE - 1: 0] raddr2_min; //read start address
input [ASIZE - 1: 0] raddr2_max; //read max address
input [8: 0] rd2_length; //read length
input rd2_load; //read register load&fifo clear
input rd2_clk; //read fifo clock

//sdram side
output [ROWSIZE - 1: 0] SA; //SDRAM address output
output [1: 0] BA; //sdram bank address
output CS_N; //sdram chip selects
output CKE; //sdram clock enable
output RAS_N; //sdram row address strobe
output CAS_N; //sdram column address strobe
output WE_N; //SDRAM WRITE ENABLE
inout [DSIZE - 1: 0] DQ; //sdram data bus
output [DSIZE - 1: 0] DQM; //sdram data lines
output SDR_CLK; //sdram clock

//user interface add by crazybing0
output Sdram_Init_Done; //sdram init done signal
input Sdram_Read_Valid; //sdram read valid:output

//internal registers/writes
//controller
reg [ASIZE - 1: 0] mADDR; //internal address
reg [8: 0] mLENGTH; //internal length
reg [ASIZE - 1: 0] waddr1; //register write address
reg [ASIZE - 1: 0] waddr2; //register write address
reg [ASIZE - 1: 0] raddr1; //register read address
reg [ASIZE - 1: 0] raddr2; //register read address
reg [1: 0] WR_MASK; //write port active mask
reg [1: 0] RD_MASK; //read port active mask
reg mWR_DONE; //flag write done,1 pulse sdr_clk
reg mRD_DONE; //flag read done,1 pulse sdr_clk
reg mWR, Pre_WR; //internal wr1 edge capture
reg mRD, Pre_RD; //internal RD edge capture

reg [9: 0] ST; //controller status
reg [1: 0] CMD; //controller command
reg PM_STOP; //flag page mode stop
reg PM_DONE; //flag page mode done
reg Read; //flag read active
reg Write; //flag write active

wire [DSIZE - 1: 0] mDATAIN; //controller data input
wire [DSIZE - 1: 0] mDATAIN1; //controller data input 1
wire [DSIZE01: 0] mDATAIN2; //controller data input 1
wire CMDACK; //controller command acknowledgement

//dram control
reg [DSIZE / 8 - 1: 0] DQM; //sdram data mask lines
reg [ROWSIZE - 1: 0] SA; //sdram address output
reg [1: 0] BA; //sdram bank address
reg CS_N; //sdram chip selects
reg CKE; //sdram clock enable
reg RAS_N; //sdram row address strobe
reg CAS_N; //sdram column address strobe
reg WE_N; //sdram write enable
wire [DSIZE - 1: 0] DQOUT; //sdram data out link
wire [ROWSIZE - 1: 0] ISA; //sdram address output
wire [1: 0] IBA; //sdram bank address
wire ICS_N; //sdram chip selects
wire ICKE; //sdram clock enable
wire IRAS_N; //sdram row address strobe
wire ICAS_N; //sdram column address strobe
wire IWE_N; //sdram write enable
//fifo control
reg OUT_VALID; //output data request to read side fifo
reg IN_REQ; //input data request to write side

// fifo
wire [8: 0] write_side_fifo_rusedw1;
wire [8: 0] write_side_fifo_rusedw2;
wire [8: 0] read_side_fifo_wusedw1;
wire [8: 0] read_side_fifo_wusedw2;

//dram internal contrpl
wire [ASIZE - 1: 0] saddr;
wire load_mode;
wire nop;
wire reada;
wire writea;
wire refresh;
wire precharge;
wire oe;
wire ref_ack;
wire ref_req;
wire init_req;
wire cm_ack;

assign SDR_CLK = OUT_CLK; //sdram output clock
control_interface control1(
                      .CLK(REF_CLK),
                      .RESET_N(RESET_N),
                      .CMD(CMD),
                      .ADDR(mADDR),
                      .REF_ACK(cm_ack),
                      .CM_ACK(cm_ack),
                      .NOP(nop),
                      .READA(reada),
                      .WRITEA(writea),
                      .REFRESH(refresh),
                      .PRECHARGE(precharge),
                      .LOAD_MODE(load_mode),
                      .SADDR(saddr),
                      .REF_REQ(ref_req),
                      .INIT_REQ(init_req),
                      .CMD_ACK(CMDACK),
                      .Sdram_Init_Done(Sdram_Init_Done)
                  );

command command(
            .CLK(REF_CLK),
            .RESET_N(RESET_N),
            .SADDR(saddr),
            .NOP(nop),
            .READA(reada),
            .WRITEA(writea),
            .REFRESH(refresh),
            .LOAD_MODE(load_mode),
            .PRECHARGE(precharge),
            .REF_REQ(ref_req),
            .INIT_REQ(init_req),
            .REF_ACK(ref_ack),
            .CM_ACK(cm_ack),
            .OE(oe),
            .PM_STOP(PM_STOP),
            .PM_DONE(PM_DONE),
            .SA(ISA),
            .BA(IBA),
            .CS_N(ICS_N),
            .CKE(ICKE),
            .RAS_N(IRAS_N),
            .CAS_N(ICAS_N),
            .WE_N(IWE_N)
        );

// sdr_data_path data+path(
//     .CLK(REF_CLK),
//     .RESET_N(RESET_N),
//     .DATAIN(mDATAIN),
//     .DM({(DSIZE/8){1'B0}}),
//     .DQOUT(DQOUT),
//     .DQM(IDQM),
// );

write_fifo1 u_write_fifo1(
                .data(wdat1),
                .wrreq(wr1),
                .wrclk(wr1_clk),
                .aclr(~RESET_N | wr1_load);
                .rdreq(IN_REQ & WR_MASK[0]),
                .rdclk(REF_CLK),
                .q(mDATAIN1),
                .rdusedw(write_side_fifo_rusedw1)
            );

write_fifo2 u_write_fifo2(
                .data(wdat2),
                .wrreq(wr2),
                .wrclk(wr2_clk),
                .aclr(~RESET_N | wr2_load),
                .rdreq(IN_REQ & WR_MASK[1]),
                .rdclk(REF_CLK),
                .q(mDATAIN2),
                .rdusedw(write_side_fifo_rusedw2)
            );

assign mDATAIN = (WR_MASK[0]) ? mDATAIN1 : mDATAIN2;

read_fifo1 u_read_fifo1(
               .data(DQ[7: 0]),                 //mdataout
               .wrreq(OUT_VALID & RD_MASK[0]),
               .wrclk(REF_CLK),
               .aclr(~RESET_N | rd1_load),
               .rdreq(rd1),
               .rdclk(rd1_clk),
               .q(rdat1),
               .wrusedw(read_side_fifo_wusedw1)
           );

read_fifo2 u_read_fifo2(
               .data(DQ),                 //mdataout
               .wrreq(OUT_VALID & RD_MASK[1]),
               .wrclk(REF_CLK),
               .aclr(~RESET_N | rd2_load),
               .rdreq(rd2),
               .rdclk(rd2_clk),
               .q(rdat2),
               .wrusedw(read_side_fifo_wusedw2)
           );

always@(posedge REF_CLK)
	begin
		SA <= (ST == SC_CL + mLENGTH) ? ROWSIZE'h200 : ISA;
		BA <= IBA;
		CS_N <= ICAS_N;
		CKE <= ICKE;
		RAS_N <= (ST == SC_CL + mLENGTH) ? 1'b0 : IRAS_N;
		CAS_N <= (ST == SC_CL + mLENGTH) ? 1'b1 : ICAS_N;
		WE_N <= (ST == SC_CL + mLENGTH) ? 1'b0 : IWE_N;
		PM_STOP <= (ST == SC_CL + mLENGTH) ? 1'b1 : 1'b0;
		PM_DONE <= (ST == SC_CL + SC_RCD + mLENGTH) ? 1'b1 : 1'b0;
		DQM <= {(DSIZE / 8){1'b0}};
	end

assign DQOUT = mDATAIN;
assign DQ = oe ? DQOUT : DSIZE'hzzzz;

always@(posedge REF_CLK or negedge RESET_N)
	begin
		if (RESET_N == 0)
			begin
				CMD <= 0;
				ST <= 0;
				Pre_RD <= 0;
				Pre_WR <= 0;
				Read <= 0;
				Write <= 0;
				OUT_VALID <= 0;
				IN_REQ <= 0;
				mWR_DONE <= 0;
				mRD_DONE <= 0;
			end
		else
			begin
				Pre_RD <= mRD;
				Pre_WR <= mWR;
				case (ST)
					0:
						begin
							if ({Pre_RD, mRD} == 2'b01)
								begin
									Read <= 1;
									Write <= 0;
									CMD <= 2'b01;
									ST <= 1;
								end
							else if ({Pre_WR, mWR} == 2'b01)
								begin
									Read <= 0;
									Write <= 1;
									CMD <= 2'b10;
									ST <= 1;
								end
						end
					1:
						begin
							if (CMDACK == 1)
								begin
									CMD <= 2'b00;
									ST <= 2;
								end
						end
					default:
						begin
							if (ST != SC_CL + SC_RCD + mLENGTH + 1)
								ST <= ST + 1'b1;
							else
								ST <= 0;
						end
				endcase
				if (Read) //read done
					begin
						if (ST == SC_CL + SC_RCD + 1)
							OUT_VALID <= 1;
						else if (ST == SC_CL + SC_RCD + mLENGTH + 1)
							begin
								OUT_VALID <= 0;
								Read <= 0;
								mRD_DONE <= 1;
							end
						else
							mRD_DONE <= 0;
					end
				if (Write) //write done
					begin
						if (ST == SC_CL - 1)
							IN_REQ <= 1;
						else if (ST == SC_CL + mLENGTH - 1)
							IN_REQ <= 0;
						else if (ST == SC_CL + SC_RCD + mLENGTH)
							begin
								Write <= 0;
								mWR_DONE <= 1;
							end
					end
				else
					mWR_DONE <= 0;
			end
	end

//internal address &length control for write
wire wr1_full = (waddr1[ASIZE - 2: 0] == waddr2_max - wr1_length);
wire wr2_bk0 = ((waddr2 <= {1'b0, waddr2_max}) & (waddr2 >= {1'b0, waddr1_max}));
wire wr2_bk1 = ((waddr2 <= {1'b1, waddr2_max}) & (waddr2 >= {1'b1, waddr2_min}));

always@(posedge REF_ACK or negedge RESET_N)
	begin
		if (!RESET_N)
			begin
				waddr1 <= waddr1_min;
				waddr2 <= {1'b0, waddr2_min};
			end
		else
			begin
				if (wr1_load)
					waddr1 <= waddr1_min;
				else if (mWR_DONE & WR_MASK[0])
					begin
						if (wr1_full)
							waddr1 <= waddr1_min;
						else
							waddr1 <= waddr1 + wr1_length;
					end
				if (wr2_load)
					waddr2 <= {1'b0, waddr2_min};
				else if (mWR_DONE & WR_MASK[1])
					begin
						if (waddr2[ASIZE - 2: 0] < waddr2_max - wr2_length)
							waddr2 <= waddr2 + wr2_length;
						else if (wr2_bk0)
							waddr2 <= {1'b0, waddr2_min};
						else if (wr2_bk1)
							waddr2 <= {1'b0, waddr2_min};
					end
			end
	end

// Internal Address & Length Control for Read;
// If enable Ping-Pong operation, Read should switch the address

wire rd1_empty = (raddr1[ASIZE - 2: 0] == raddr1_max - rd1_length);
//wire rd2_bk0 = ((raddr2<={1'b0,raddr2_max}) & (raddr2>={1'b0,raddr2_min}));
//wire rd2_bk1 = ((raddr2<={1'b1,raddr2_max}) & (raddr2>={1'b1,raddr2_min}));

always@(posedge REF_CLK or negedge RESET_N)
	begin
		if (!RESET_N)
			begin
				raddr1 <= raddr1_min;
				raddr2 <= {1'b1, raddr2_min};
			end
		else
			begin
				if (rd1_load)
					raddr1 <= raddr1_min;
				else if (mRD_DONE & RD_MASK[0])
					begin
						if (rd1_empty)
							raddr1 <= raddr1_min;
						else
							raddr1 <= raddr1 + rd1_length;
					end
				if (rd2_load)
					raddr2 <= {1'b1, raddr2_min};
				else if (mRD_DONE & RD_MASK[1])
					begin
						if (raddr2[ASIZE - 2: 0] < raddr2_max - rd2_length)
							raddr2 <= raddr2 + rd2_length;
						else if (wr2_bk0)
							raddr2 <= {1'b0, raddr2_min};
						else if (wr2_bk1)
							raddr2 <= {1'b0, raddr2_min};
					end
			end
	end
//auto read//write control
always@(posedge REF_CLK or negedge RESET_N)
	begin
		if (!RESET_N)
			begin
				mWR <= 0;
				mRD <= 0;
				mADDR <= 0;
				mLENGTH <= 0;
				WR_MASK <= 0;
				RD_MASK <= 0;
			end
		else
			begin
				if ((mWR == 0) & (mRD == 0) & (ST == 0) & (WR_MASK == 0) & (RD_MASK == 0))
					begin
						if ((read_side_fifo_wusedw1 < rd1_length) && (rd1_load == 0) && (Sdram_Read_Valid == 1'b1))
							begin
								mADDR <= raddr1;
								mLENGTH <= rd1_length;
								WR_MASK <= 0;
								RD_MASK <= 2'b01;
								mWR <= 0;
								mRD <= 1;
							end
						if ((read_side_fifo_wusedw2 < rd2_length) && (rd2_load == 0) && (Sdram_Read_Valid == 1'b1))
							begin
								mADDR <= raddr2;
								mLENGTH <= rd2_length;
								WR_MASK <= 0;
								RD_MASK <= 2'b10;
								mWR <= 0;
								mRD <= 1;
							end
						if ((write_side_fifo_rusedw1 >= wr1_length) & (wr1_load == 0))
							begin
								mADDR <= waddr1;
								mLENGTH <= wr1_length;
								WR_MASK <= 2'b01;
								RD_MASK <= 0;
								mWR <= 1;
								mRD <= 0;
							end
						if ((write_side_fifo_rusedw2 >= wr2_length) & (wr2_load == 0))
							begin
								mADDR <= waddr2;
								mLENGTH <= wr2_length;
								WR_MASK <= 2'b10;
								RD_MASK <= 0;
								mWR <= 1;
								mRD <= 0;
							end
					end
				if (mWR_DONE)
					begin
						WR_MASK <= 0;
						mWR <= 0;
					end
				if (mRD_DONE)
					begin
						RD_MASK <= 0;
						mRD <= 0;
					end
			end
	end
endmodule
